#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>

#define MAX_EVENTS 1024
#define BUFFER_SIZE 4096

#define BATCH_SIZE 32 // 每批次处理的最大发送消息数, 这个最好不要超过64，超过之后丢包会变严重
#define RECV_BATCH_SIZE 64 // 每批次处理的最大接收消息数

// 单核linux的极限pps（包每秒）一般在150k~300k, 再往上优化单核很难:  https://linux.cn/article-5724-1.html

typedef long counter_t;

void set_nonblocking(int sockfd)
{
    int opts = fcntl(sockfd, F_GETFL);
    if (opts < 0) {
        perror("fcntl(F_GETFL)");
        exit(EXIT_FAILURE);
    }
    opts = opts | O_NONBLOCK;
    if (fcntl(sockfd, F_SETFL, opts) < 0) {
        perror("fcntl(F_SETFL)");
        exit(EXIT_FAILURE);
    }
}

class MyTimer {
public:
    struct timespec start;
    struct timespec end;

    MyTimer()
    {
        clock_gettime(CLOCK_MONOTONIC, &start); // 开始计时
    }

    double elapsed()
    {
        clock_gettime(CLOCK_MONOTONIC, &end); // 开始计时
        double time_taken = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
        return time_taken;
    }
};

class EmuFpga {
public:
    void start(int port)
    {
        int udp_fd, epoll_fd;
        struct sockaddr_in serv_addr, cli_addr[BATCH_SIZE];
        struct epoll_event ev, events[MAX_EVENTS];
        char buffer[BATCH_SIZE][BUFFER_SIZE];
        struct mmsghdr msgs[BATCH_SIZE];
        struct iovec iovecs[BATCH_SIZE];
        socklen_t addr_len[BATCH_SIZE];
        int num_msgs;

        // 创建 UDP socket
        if ((udp_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
            perror("socket");
            exit(EXIT_FAILURE);
        }

        set_nonblocking(udp_fd);

        // 设置服务器地址结构
        memset(&serv_addr, 0, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(port);

        // 绑定 socket
        if (bind(udp_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
            perror("bind");
            exit(EXIT_FAILURE);
        }
        printf("server port: %d\n", ntohs(serv_addr.sin_port));

        // 创建 epoll 实例
        if ((epoll_fd = epoll_create1(0)) < 0) {
            perror("epoll_create1");
            exit(EXIT_FAILURE);
        }

        // 将 UDP socket 加入 epoll 监听列表
        ev.events = EPOLLIN;
        ev.data.fd = udp_fd;
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, udp_fd, &ev) < 0) {
            perror("epoll_ctl: udp_fd");
            exit(EXIT_FAILURE);
        }

        long total_recv_pkt = 0;
        long recv_bytes = 0;
        MyTimer timer;
        double last_elapse = 0;
        long last_total_recv_pkt = 0;
        while (1) {
            int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
            if (nfds < 0) {
                perror("epoll_wait");
                exit(EXIT_FAILURE);
            }

            for (int i = 0; i < nfds; i++) {
                if (events[i].events & EPOLLIN) {
                    // 初始化 mmsghdr 结构体数组
                    for (int j = 0; j < BATCH_SIZE; j++) {
                        addr_len[j] = sizeof(cli_addr[j]);

                        iovecs[j].iov_base = buffer[j];
                        iovecs[j].iov_len = BUFFER_SIZE;

                        memset(&msgs[j], 0, sizeof(struct mmsghdr));
                        msgs[j].msg_hdr.msg_iov = &iovecs[j];
                        msgs[j].msg_hdr.msg_iovlen = 1;
                        msgs[j].msg_hdr.msg_name = &cli_addr[j];
                        msgs[j].msg_hdr.msg_namelen = addr_len[j];
                    }

                    // 批量接收消息
                    num_msgs = recvmmsg(events[i].data.fd, msgs, BATCH_SIZE, 0, NULL);
                    if (num_msgs < 0) {
                        perror("recvmmsg");
                        continue;
                    }

                    total_recv_pkt += num_msgs;
                    // if (rand() % 1000 > 500) {
                    //     // 模拟丢包
                    //     continue;
                    // }
                    if (total_recv_pkt % 20000 == 0) {
                        double elapsed_time = timer.elapsed();
                        double recv_pps = total_recv_pkt / 1000.0 / elapsed_time;
                        long cur_pkt = (total_recv_pkt - last_total_recv_pkt);
                        double cur_elapsed_time = elapsed_time - last_elapse;
                        double cur_recv_pps = cur_pkt / 1000.0 / cur_elapsed_time;

                        // 将IP地址转换为字符串形式
                        char ip_str[32];
                        if (inet_ntop(AF_INET, &cli_addr[0].sin_addr, ip_str, sizeof(ip_str)) == NULL) {
                            perror("inet_ntop failed");
                            close(udp_fd);
                            exit(EXIT_FAILURE);
                        }

                        printf("recv addr: %s, port: %ld, "
                               "recv pkt: %ld, pps: %.2fKpps, time: %.2fs, "
                               "cur pkt: %ld, pps: %.2fKpps, time: %.2fs\n",
                            ip_str, ntohs(cli_addr[0].sin_port),
                            total_recv_pkt, recv_pps, elapsed_time,
                            cur_pkt, cur_recv_pps, cur_elapsed_time);
                    }

                    sendmmsg(events[i].data.fd, msgs, num_msgs, 0);

                    // // 回显接收到的消息
                    // for (int j = 0; j < num_msgs; j++) {
                    //     sendmmsg(events[i].data.fd, &msgs[j], 1, 0);
                    // }
                }
            }
        }

        close(udp_fd);
        close(epoll_fd);
    }
};

class PcSender {
    int udp_fd;
    int epoll_fd;
    struct sockaddr_in local_addr, fpga_addr;
    struct epoll_event ev, events[MAX_EVENTS];
    socklen_t addr_len = sizeof(fpga_addr);

    int fpga_ip; // 远程数据的ip
    const char* fpga_ip_str;
    int fpga_port; // 远程数据的端口

    int local_ip; // 接收数据的ip
    const char* local_ip_str;
    int local_port; // 接收数据的端口

    char batch_recv_buffer[RECV_BATCH_SIZE][BUFFER_SIZE];
    struct mmsghdr batch_recv_msgs[RECV_BATCH_SIZE];
    struct iovec batch_recv_iovecs[RECV_BATCH_SIZE];
    socklen_t batch_addr_len[RECV_BATCH_SIZE];
    struct sockaddr_in batch_recv_fpga_addr[RECV_BATCH_SIZE]; // 不用，接收是所使用的临时地址

    void* new_send_mmap_buff; // mmap时所分配的地址
    char batch_send_buffer[BATCH_SIZE][BUFFER_SIZE];
    struct mmsghdr batch_send_msgs[BATCH_SIZE];
    struct iovec batch_send_iovecs[BATCH_SIZE];

    int epoll_base_ms = 20; // 默认20ms超时
    int epoll_wait_ms = 0;

    // 计时器
    MyTimer timer;
    // 总计数
    counter_t total_send_pkt = 0, total_recv_pkt = 0, err_recv_pkt = 0; // 当前所有收发包、错误包的总数
    counter_t total_send_bytes = 0, total_recv_bytes = 0; // 当前说有收发的数据量
    counter_t last_total_recv_pkt = 0, last_total_send_pkt = 0; // 倒数第二次及之前的收发包总数
    // 每轮计数
    double last_elapse = 0; // 最近完成一次的时间消耗（秒）
    counter_t bubu_pkt = 0, bubu_n = 0, bubu_avg = 0; // 丢包后的修正值

    counter_t this_recv_pkt = 0, this_send_pkt = 0; // 最后一次收发包数
    counter_t last_recv_pkt = 0, last_send_pkt = 0; // 倒数第二次收发包数

    int timeout_tick = 0; // 连续接收超时计数器
    int send_pkt_acc_delta = 0; // 发送数据包修正因子

    /*
        将会通过上述统计数据进行动态调整发包速度
     */

    void* new_mmap(int size)
    {
        int fd;
        void* mapped;
        // size_t size = 4096; // 映射大小为 4KB
        // 打开 /dev/zero
        fd = open("/dev/zero", O_RDWR);
        if (fd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }

        // 使用 mmap 映射 /dev/zero
        // mapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        mapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

        if (mapped == MAP_FAILED) {
            perror("mmap");
            close(fd);
            exit(EXIT_FAILURE);
        }

        // 关闭文件描述符，因为 mmap 映射已经完成
        close(fd);
        return mapped;
    }

public:
    PcSender(const char* _fpga_ip_str, int _fpga_port)
    {
        fpga_ip_str = _fpga_ip_str;
        fpga_port = _fpga_port;

        // local_ip_str = "127.0.0.1";
        local_ip_str = "0.0.0.0";
        local_port = fpga_port + 1;
        local_port = 0;

        // 创建 UDP socket
        if ((udp_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
            // if ((udp_fd = socket(AF_INET, SOCK_DGRAM, MSG_ZEROCOPY)) < 0) {

            perror("socket");
            exit(EXIT_FAILURE);
        }

        set_nonblocking(udp_fd);

#ifdef MMAP_
        // 申请能够包含所有buf的整数倍4KB页面
        new_send_mmap_buff = new_mmap(((BATCH_SIZE * BUFFER_SIZE / 4096) + 1) * 4096);
#endif

        // int optval = 1;
        // socklen_t optlen = sizeof(optval);
        // // Set socket options (example: enabling SO_REUSEADDR)
        // if (setsockopt(udp_fd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen) < 0) {
        //     perror("setsockopt SO_REUSEADDR");
        //     close(udp_fd);
        //     return;
        // }
        // if (setsockopt(udp_fd, SOL_SOCKET, SO_ZEROCOPY, &optval, optlen)) {
        //     perror("setsockopt zerocopy");
        //     close(udp_fd);
        //     return;
        // }

        // if (setsockopt(udp_fd, SOL_SOCKET, MSG_ZEROCOPY, &optval, optlen) < 0) {
        //     perror("setsockopt SO_REUSEADDR");
        //     close(udp_fd);
        //     return;
        // }

        // 设置服务器地址结构
        memset(&local_addr, 0, sizeof(local_addr));
        local_addr.sin_family = AF_INET;
        // 转换 IPv4 地址
        if (inet_pton(AF_INET, local_ip_str, &local_addr.sin_addr.s_addr) <= 0) {
            perror("inet_pton for IPv4");
            exit(EXIT_FAILURE);
        }
        // local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        local_addr.sin_port = htons(local_port);

        memset(&fpga_addr, 0, sizeof(fpga_addr));
        fpga_addr.sin_family = AF_INET;
        if (inet_pton(AF_INET, fpga_ip_str, &fpga_addr.sin_addr.s_addr) <= 0) {
            perror("inet_pton for IPv4");
            exit(EXIT_FAILURE);
        }
        // fpga_addr.sin_addr.s_addr = htonl(fpga_ip);
        fpga_addr.sin_port = htons(fpga_port);
    }

    ~PcSender()
    {
#ifdef MMAP_
        if (new_send_mmap_buff != NULL) {
            munmap(new_send_mmap_buff, ((BATCH_SIZE * BUFFER_SIZE / 4096) + 1) * 4096);
        }
#endif
    }

    void start()
    {
        // 绑定 socket
        if (bind(udp_fd, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) {
            perror("bind");
            exit(EXIT_FAILURE);
        }

        // 使用getsockname获取分配的端口号和IP地址
        socklen_t addr_len;
        struct sockaddr_in bind_addr;
        if (getsockname(udp_fd, (struct sockaddr*)&bind_addr, &addr_len) == -1) {
            perror("getsockname failed");
            close(udp_fd);
            exit(EXIT_FAILURE);
        }

        // 将IP地址转换为字符串形式
        char ip_str[32];
        if (inet_ntop(AF_INET, &bind_addr.sin_addr, ip_str, sizeof(ip_str)) == NULL) {
            perror("inet_ntop failed");
            close(udp_fd);
            exit(EXIT_FAILURE);
        }

        printf("Bound to IP: %s, Port: %d\n", ip_str, ntohs(local_addr.sin_port));

        // 创建 epoll 实例
        if ((epoll_fd = epoll_create1(0)) < 0) {
            perror("epoll_create1");
            exit(EXIT_FAILURE);
        }

        // 将 UDP socket 加入 epoll 监听列表
        ev.events = EPOLLIN;
        ev.data.fd = udp_fd;
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, udp_fd, &ev) < 0) {
            perror("epoll_ctl: udp_fd");
            exit(EXIT_FAILURE);
        }
        epoll_wait_ms = epoll_base_ms;
        int timeout = 0;
        this_recv_pkt = 0;
        while (1) {
            last_send_pkt = this_send_pkt;
            last_recv_pkt = this_recv_pkt;
            this_recv_pkt = 0;
            this_send_pkt = 0;
            send_control(timeout);

            timeout = 0;
            // 这里需要使用超时机制，否则将会死等返回包
            // int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
            int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, epoll_wait_ms); // timeout 100ms
            if (nfds == 0) {
                timeout = 1;
                if (epoll_wait_ms > (epoll_base_ms * 20)) {
                    printf("exit TIMEOUT all: %ldms\n", epoll_wait_ms);
                    update_statistic();
                    break;
                }
                // printf("DEBUG: timeout.....\n");
                continue;
            } else if (nfds < 0) {
                perror("epoll_wait");
                exit(EXIT_FAILURE);
            }

            for (int i = 0; i < nfds; i++) {
                int sock_fd = events[i].data.fd;
                // if (events[i].events & EPOLLOUT) {
                //     if (sock_fd == udp_fd) {
                //         printf("EPOLLOUT.....\n");
                //         do_send(udp_fd);
                //     }
                // }
                if (events[i].events & EPOLLIN) {
                    recv_control(sock_fd);
                } else {
                    printf("========\n");
                }
            }
        }
        close(udp_fd);
        close(epoll_fd);
    }

    int send_control_1()
    {
        // 收包比较慢的时候，比较平衡
        int expect_send_pkt = 1;
        // 这个控制算法貌似效率比较高，适应场景比较广
        // 但这里还需要改进，以便更加广泛的适用不同的网络环境
        if ((last_send_pkt - last_recv_pkt) >= last_recv_pkt) {
            // 当前收包较慢，需要减少发包速率，累积了至少最近一次收包数量的包还未处理
            // 减少一倍的发包速度，使其达到平衡
            expect_send_pkt = last_recv_pkt / 2 - 2; // 这里的关键在于除2， 减2后更好一点
            send_pkt_acc_delta = -1 * last_recv_pkt / 2; // 这个不能再减，否则在收包数量低的时候会导致为负数，且由于收包为1,使再次发包的值为负，从而导致在存在丢包的时候，出现必须超时才能恢复，严重降低性能的问题
            // update_statistic();
        } else {
            expect_send_pkt = last_recv_pkt + send_pkt_acc_delta;
            send_pkt_acc_delta++;
        }
        return expect_send_pkt;
    }

    counter_t send_call_tick = 0;
    counter_t tick_record_send_pkt = 0;
    counter_t tick_record_recv_pkt = 0;

    counter_t last_k_tick_avg_recv_pkt = 0;
    int tick_k = 0;
    int tick_max_k = 1000;
    int each_k_max_tick = 50;

    int send_control_2()
    {
        int expect_send_pkt = 1;

        send_call_tick++;
        tick_record_recv_pkt += last_recv_pkt;
        tick_record_send_pkt += last_send_pkt;

        counter_t tick_avg_send_pkt = tick_record_send_pkt / send_call_tick;
        counter_t tick_avg_recv_pkt = tick_record_recv_pkt / send_call_tick;

        if (send_call_tick >= each_k_max_tick) {
            if (tick_k >= tick_max_k) {
                last_k_tick_avg_recv_pkt = (last_k_tick_avg_recv_pkt * (tick_k - 1) + tick_record_recv_pkt) / (tick_k);
            } else {
                last_k_tick_avg_recv_pkt = (last_k_tick_avg_recv_pkt * tick_k + tick_record_recv_pkt) / (tick_k + 1);
                tick_k++;
            }
        }

        if (tick_avg_recv_pkt == 0 || last_k_tick_avg_recv_pkt == 0) {
            expect_send_pkt = 1;
        } else if (tick_avg_send_pkt > last_k_tick_avg_recv_pkt) {
            expect_send_pkt = last_k_tick_avg_recv_pkt / 2 + 1;
        } else if (tick_avg_send_pkt > tick_avg_recv_pkt) {
            expect_send_pkt = tick_avg_recv_pkt / 2 + 1;
            send_pkt_acc_delta = 0 - tick_avg_recv_pkt / 2;
        } else {
            expect_send_pkt = tick_avg_recv_pkt + 1;
        }

        if (send_call_tick >= each_k_max_tick) {
            send_call_tick = 0;
            tick_record_recv_pkt = 0;
            tick_record_send_pkt = 0;
        }
        return expect_send_pkt;
    }

    void send_control(int timeout)
    {
        int expect_send_pkt = 1;
        if (total_send_pkt == 0) {
            // expect_send_pkt = BUFFER_SIZE;
            expect_send_pkt = 1;
        } else if (timeout) {
            // expect_send_pkt = BUFFER_SIZE;
            expect_send_pkt = 1;
            timeout_tick++;
        } else {
            timeout_tick = 0;
            // expect_send_pkt = send_control_1();
            expect_send_pkt = send_control_2();
        }

        // update_statistic();

        // if (total_send_pkt % 200000 == 0) {
        //     printf("DEBUG send: hold: %ld, timeoout: %d, timeout_tick: %d,  total_send_pkt: %ld, %d\n",
        //         (last_send_pkt - last_recv_pkt), timeout, timeout_tick, total_send_pkt);
        // }

        epoll_wait_ms = epoll_base_ms + timeout_tick * epoll_base_ms;

        if (total_send_pkt >= 1000 * 1000 * 1) {
            // 测试只发送这么多包
            return;
        }
        do_send(udp_fd, expect_send_pkt);
    }

    void recv_control(int sock_fd)
    {
        // 主要的控制算法在send中处理，recv中只需简单保证发送不被饿死、接收尽可能迅速即可
        // 动态调整发送、接收速率
        int i = 0;
        int res = 0;
        int bubu_rato = (total_send_pkt - total_recv_pkt - bubu_avg) > (RECV_BATCH_SIZE * 100);
        do {
            res = do_recive(sock_fd);
            i++;
        } while (bubu_rato && i < 5 && res > 0);

        // printf("DEBUG: %d, this_recv_pkt: %ld\n", i, this_recv_pkt);

        bubu_pkt = total_send_pkt - total_recv_pkt;
        bubu_n++;
        if (bubu_n > 10) {
            // 修正丢包的算法，使发包保持稳定
            bubu_avg = bubu_pkt / bubu_n;
            bubu_n = 0;
            bubu_pkt = 0;
        }
    }

#define offset(p, off) (((char*)(p)) + (off))
#define buff_p(p, inx) offset(p, BUFFER_SIZE* inx)

    void do_send(int sock_fd, int expect_send_pkt)
    {

        if (expect_send_pkt > BATCH_SIZE)
            expect_send_pkt = BATCH_SIZE;
        else if (expect_send_pkt <= 0) {
            expect_send_pkt = 0;
        }
        last_send_pkt = this_send_pkt;
        // 初始化 mmsghdr 结构体数组
        for (int j = 0; j < expect_send_pkt; j++) {
            total_send_pkt++;
            this_send_pkt++;

            total_send_bytes += BUFFER_SIZE;

#ifdef MMAP_
            batch_send_iovecs[j].iov_base = buff_p(new_send_mmap_buff, j);
#else
            batch_send_iovecs[j].iov_base = batch_send_buffer[j];
#endif
            batch_send_iovecs[j].iov_len = BUFFER_SIZE;

#ifdef MMAP_
            calc_crc(buff_p(new_send_mmap_buff, j), BUFFER_SIZE); // 模拟每次的耗时计算
#else
            calc_crc(batch_send_buffer[j], BUFFER_SIZE); // 模拟每次的耗时计算
#endif

            memset(&batch_send_msgs[j], 0, sizeof(struct mmsghdr));
            batch_send_msgs[j].msg_hdr.msg_iov = &batch_send_iovecs[j];
            batch_send_msgs[j].msg_hdr.msg_iovlen = 1;
            batch_send_msgs[j].msg_hdr.msg_name = &fpga_addr;
            batch_send_msgs[j].msg_hdr.msg_namelen = sizeof(fpga_addr);
            batch_send_msgs[j].msg_len = BUFFER_SIZE; // send_data bytes size
        }
        sendmmsg(sock_fd, batch_send_msgs, expect_send_pkt, 0);
        // sendmmsg(sock_fd, batch_send_msgs, BATCH_SIZE, MSG_ZEROCOPY);

        // for (int i = 0; i < 200 - (total_send_pkt - total_recv_pkt); i++) {
        //     total_send_bytes += BUFFER_SIZE;
        //     total_send_pkt++;
        //     sendto(sock_fd, send_buffer, BUFFER_SIZE, 0, (struct sockaddr*)&fpga_addr, addr_len);
        // }
    }

    int do_recive(int sock_fd)
    {
        // 初始化 mmsghdr 结构体数组
        for (int j = 0; j < RECV_BATCH_SIZE; j++) {
            batch_addr_len[j] = sizeof(batch_recv_fpga_addr[j]);
            batch_recv_iovecs[j].iov_base = batch_recv_buffer[j];
            batch_recv_iovecs[j].iov_len = BUFFER_SIZE;

            memset(&batch_recv_msgs[j], 0, sizeof(struct mmsghdr));
            batch_recv_msgs[j].msg_hdr.msg_iov = &batch_recv_iovecs[j];
            batch_recv_msgs[j].msg_hdr.msg_iovlen = 1;
            batch_recv_msgs[j].msg_hdr.msg_name = &batch_recv_fpga_addr[j];
            batch_recv_msgs[j].msg_hdr.msg_namelen = addr_len;
        }

        // 批量接收消息
        int num_msgs = recvmmsg(sock_fd, batch_recv_msgs, RECV_BATCH_SIZE, 0, NULL);
        if (num_msgs < 0) {
            perror("recvmmsg");
            return 0;
        }

        // 回显接收到的消息
        for (int j = 0; j < num_msgs; j++) {
            total_recv_pkt++;
            this_recv_pkt++;

            total_recv_bytes += batch_recv_msgs[j].msg_hdr.msg_iov->iov_len;
            recv_load(batch_recv_buffer[j], batch_recv_msgs[j].msg_len);

            if (total_recv_pkt % 200000 == 0) {
                printf("DEBUG: msg_iovlen: %ld, iov_len: %lu, msg_len: %lu"
                       "recv addr: %ld, port: %d\n",
                    batch_recv_msgs[j].msg_hdr.msg_iovlen,
                    batch_recv_msgs[j].msg_hdr.msg_iov->iov_len,
                    batch_recv_msgs[j].msg_len,
                    batch_recv_fpga_addr[j].sin_addr,
                    ntohs(batch_recv_fpga_addr[j].sin_port));

                update_statistic();
            }
        }
        if (num_msgs <= RECV_BATCH_SIZE)
            return 0;
        return 1;
    }

    // 准备发送数据，发送负载
    void calc_crc(char* buf, int buf_len)
    {
        for (int i = 0; i < buf_len; i++) {
            buf[i] = i;
        }
    }

    // 接收负载
    void recv_load(char* buf, int buf_len)
    {
        // return;
        volatile int res = 0;
        char tres;
        for (int i = 0; i < buf_len; i++) {
            tres = buf[i] ^ tres;
        }
        res = tres;
    }

    void update_statistic()
    {

        // 总平均信息统计
        double elapsed_time = timer.elapsed();

        double send_size_mb = total_send_bytes / 1024.0 / 1024;
        double recv_size_mb = total_recv_bytes / 1024.0 / 1024;
        double send_bw = total_send_bytes / 1024.0 / 1024 / elapsed_time;
        double recv_bw = total_recv_bytes / 1024.0 / 1024 / elapsed_time;
        double send_pps = double(total_send_pkt) / 1000 / elapsed_time;
        double recv_pps = double(total_recv_pkt) / 1000 / elapsed_time;
        double success_pkt_rate = double(total_recv_pkt) / total_send_pkt * 100;
        // 本轮信息统计
        double cur_elapsed_time = elapsed_time - last_elapse;
        double cur_send_pps = double(total_send_pkt - last_total_send_pkt) / 1000 / cur_elapsed_time;
        double cur_recv_pps = double(total_recv_pkt - last_total_recv_pkt) / 1000 / cur_elapsed_time;

        printf("recv...: total_send_pkt: %ld, total_recv_pkt: %ld, "
               "send_size: %.2fMB, recv_size: %.2fMB, "
               "speed: s: %.2fMB/s r:%.2fMB/s <-> "
               "avg: s:%.2fKpps r:%.2fKpps, "
               "cur: s:%.2fKpps r:%.2fKpps, "
               "time: t: %.2fs, c: %.2fs, "
               "last: s: %ld, r: %ld, "
               "rate: %.2f%%, bubu_pkt: %ld\n",
            total_send_pkt, total_recv_pkt,
            send_size_mb, recv_size_mb,
            send_bw, recv_bw,
            send_pps, recv_pps,
            cur_send_pps, cur_recv_pps,
            elapsed_time, cur_elapsed_time,
            last_send_pkt, last_recv_pkt,
            success_pkt_rate, bubu_pkt);

        last_elapse = elapsed_time;
        last_total_recv_pkt = total_recv_pkt;
        last_total_send_pkt = total_send_pkt;
    }
};

int main(int argc, char* argv[])
{
    if (argc != 4) {
        printf("Usage: test [fpga|pc] ip port\n");
        return 1;
    }
    if (strcmp("fpga", argv[1]) == 0) {
        EmuFpga().start(std::atoi(argv[3]));
    } else if (strcmp("pc", argv[1]) == 0) {
        PcSender(argv[2], std::atoi(argv[3])).start();
    } else {
        printf("Error: command error [%s]\n", argv[1]);
        return 1;
    }
    return 0;
}
